package com.alinakipoglu.asprocedural
{
	import flash.utils.Dictionary;
	
	import com.alinakipoglu.asprocedural.interfaces.IPorceduralContext;
	import com.alinakipoglu.asprocedural.interfaces.IProceduralContextUpdater;
	import com.alinakipoglu.asprocedural.interfaces.IIDStack;
	import com.alinakipoglu.asprocedural.interfaces.IProcedure;
	
	public class ProceduralContext implements IPorceduralContext
	{
		public static const DEFAULT_STACK_SIZE	:uint				= 5;
		
		private var m_procedures				:Vector.<IProcedure>;
		private var m_procedureByName			:Dictionary;
		private var m_idStack					:IDStack;
		private var m_updater					:IProceduralContextUpdater;
		private var m_initProcedure				:IProcedure;
		private var m_resetProcedure			:IProcedure;
		
		public function ProceduralContext(_updater:IProceduralContextUpdater = null)
		{
			m_procedures		= new Vector.<IProcedure>();
			m_procedureByName	= new Dictionary();
			m_idStack			= new IDStack(DEFAULT_STACK_SIZE);
			m_updater			= _updater;
			
			if(!m_updater)
			{
				m_updater		= new DefaultContextUpdater();
			}
		}
		
		public function get idStack():IIDStack
		{
			return m_idStack;
		}
		
		public function get procedures():Vector.<IProcedure>
		{
			return m_procedures;
		}
		
		public function get idStackSize():uint
		{
			return m_idStack.stackSize;
		}
		
		public function set idStackSize(_value:uint):void
		{
			m_idStack.stackSize	= _value;
		}
		
		public function get updater():IProceduralContextUpdater
		{
			return m_updater;
		}
		
		public function set updater(_value:IProceduralContextUpdater):void
		{
			m_updater			= _value;
		}
		
		public function get initProcedure():IProcedure
		{
			return m_initProcedure;
		}
		
		public function set initProcedure(_value:IProcedure):void
		{
			m_initProcedure		= _value;
		}
		
		public function get resetProcedure():IProcedure
		{
			return m_resetProcedure;
		}
		
		public function set resetProcedure(_value:IProcedure):void
		{
			m_resetProcedure	= _value;
		}
		
		public function addProcedure(_procedure:IProcedure):void
		{
			if(m_procedures.indexOf(_procedure) == -1)
			{
				m_procedures[m_procedures.length]	= _procedure;
				m_procedureByName[_procedure.id]	= _procedure;
			}
		}
		
		public function removeProcedure(_procedure:IProcedure):void
		{
			var _procedureIndex	:uint	= m_procedures.indexOf(_procedure);
			
			if(_procedureIndex != -1)
			{
				m_procedures.splice(_procedureIndex, 1);
				m_procedureByName[_procedure.id]	= null;
			}
		}
		
		public function getProcedureByID(_id:String):IProcedure
		{
			return m_procedureByName[_id];
		}
		
		public function init():void
		{
			if(m_initProcedure)
			{
				m_initProcedure.execute(this);
				
				m_idStack.push(m_initProcedure.id);
			}
		}
		
		public function reset():void
		{
			if(m_resetProcedure)
			{
				m_resetProcedure.execute(this);
			}
			
			m_idStack.clear();
		}
		
		public function update():void
		{
			if(m_updater)
			{
				m_updater.update(this);
			}
		}
	}
}